14. Running motion with code
Kotlin SM A15 Collapsing Toolbars Step 8 Activity
Running motion with code
MotionLayout can be used to build rich animations when used with CoordinatorLayout. In this step, you'll build a collapsible header using MotionLayout.
After you complete this step you'll have built this animation.
Explore the existing code
- To get started, open
layout/activity_step8.xml. - In
layout/activity_step8.xml,you see that a workingCoordinatorLayoutandAppBarLayoutis already built.
<!-- layout/activity_step8.xml -->
<androidx.coordinatorlayout.widget.CoordinatorLayout
...>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_layout"
android:layout_width="match_parent"
android:layout_height="180dp">
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/motion_layout"
... >
…
</androidx.constraintlayout.motion.widget.MotionLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
...
app:layout_behavior="@string/appbar_scrolling_view_behavior" >
...
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
This layout uses a CoordinatorLayout to share scrolling information between the NestedScrollView and the AppBarLayout. So, when the NestedScrollView scrolls up it will tell the AppBarLayout about the change. That's how you implement a collapsing toolbar like this on Android - the scrolling of the text will be "coordinated" with the collapsing header.
The motion scene that @id/motion_layout points to is similar to the motion scene in the last step. However, the OnSwipe declaration was removed to enable it to work with CoordinatorLayout.
- Run the app and go to Step 8. You see that when you scroll the text, the moon does not move.
Make the MotionLayout scroll
To make the MotionLayout view scroll as soon as the NestedScrollView scrolls, add app:minHeight and app:layout_scrollFlags to the MotionLayout.
<!-- layout/activity_step8.xml -->
<!-- Add minHeight and layout_scrollFlags to the MotionLayout -->
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/motion_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/m_eightyone"
app:layoutDescription="@xml/step8"
app:motionDebug="SHOW_PATH"
android:minHeight="80dp"
app:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed" >
- Run the app again and got to Step 8. You see that the
MotionLayoutcollapses as you scroll up. However, the animation does not progress based on the scroll behavior yet.
Move the motion with code
Open Step8Activity.kt . Edit the coordinateMotion() function to tell MotionLayout about the changes in scroll position.
// Step8Activity.kt
// TODO: set progress of MotionLayout based on an AppBarLayout.OnOffsetChangedListener
val appBarLayout: AppBarLayout = findViewById(R.id.appbar_layout)
val motionLayout: MotionLayout = findViewById(R.id.motion_layout)
val listener = AppBarLayout.OnOffsetChangedListener { unused, verticalOffset ->
val seekPosition = -verticalOffset / appBarLayout.totalScrollRange.toFloat()
motionLayout.progress = seekPosition
}
appBarLayout.addOnOffsetChangedListener(listener)
This code will register a OnOffsetChangedListener that will be called every time the user scrolls with the current scroll offset.
MotionLayoutsupports seeking its transition by setting the progress property. To convert between a verticalOffset and a percentage progress, divide by the total scroll range.
MotionLayout can seek to a specific point in the animation in code.
Do this by setting motionLayout.progress. MotionLayout will immediately "jump" to the position that was specified.
So, for example, if you set the progress to 0.43, MotionLayout will jump to 43% through the animation.
Try out the animation
Deploy the app again and run the Step 8 animation. You see that MotionLayout will progress the animation based on the scroll position.
It's possible to build custom dynamic collapsing toolbar animations using MotionLayout. By using a sequence of KeyFrames you can achieve very bold effects.
AppBarLayout does not resize the MotionLayout.
The MotionLayout view will be moved partially offscreen when AppBarLayout collapses it. It will not be resized, but just moved up. If you have constraints to the top of the MotionLayout, they will be offscreen at the end of the animation. To work with AppBarLayout ensure end constraints are anchored to the bottom of the parent.